package shake.deployment;

import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.util.HashSet;
import java.util.Set;

import javassist.bytecode.AnnotationsAttribute;
import javassist.bytecode.ClassFile;
import shake.annotation.Component;
import shake.annotation.StereoType;

public class ComponentScanner extends URLScanner {

	Set<String> componentsClasses = new HashSet<String>();

	public Set<String> getComponentsClasses() {
		return componentsClasses;
	}

	protected javassist.bytecode.annotation.Annotation[] getAnnotations(
			ClassFile classFile) {
		AnnotationsAttribute visible = (AnnotationsAttribute) classFile
				.getAttribute(AnnotationsAttribute.visibleTag);
		if (visible == null) {
			return new javassist.bytecode.annotation.Annotation[0];
		}
		return visible.getAnnotations();
	}

	protected boolean hasAnnotation(ClassFile classFile,
			Class<? extends Annotation> annotationType) {
		AnnotationsAttribute visible = (AnnotationsAttribute) classFile
				.getAttribute(AnnotationsAttribute.visibleTag);
		if (visible != null) {
			return visible.getAnnotation(annotationType.getName()) != null;
		}
		return false;
	}

	@Override
	protected void handleItem(String name, ClassLoader loader) {
		if (name.endsWith(".class")) {
			ClassFile clazzFile = getClassFile(name, loader);
			if (hasAnnotation(clazzFile, Component.class)) {
				componentsClasses.add(toClassName(name));
				return;
			}
			for (javassist.bytecode.annotation.Annotation an : getAnnotations(clazzFile)) {
				try {
					Class clazz = loader.loadClass(an.getTypeName());
					if (clazz.isAnnotationPresent(StereoType.class)) {
						componentsClasses.add(toClassName(name));
						// System.out.println(toClassName(name));
						return;
					}
				} catch (ClassNotFoundException e) {
					// throw new RuntimeException(e);
					e.printStackTrace();
				}
			}

		}
	}

	private String toClassName(String name) {
		int len = name.length();
		return name.substring(0, len - 6).replace("/", ".");
	}

	protected ClassFile getClassFile(String name, ClassLoader classLoader) {
		if (name == null) {
			throw new NullPointerException("name cannot be null");
		}
		InputStream stream = classLoader.getResourceAsStream(name);
		if (stream == null) {
			throw new IllegalStateException("Cannot load " + name + " from "
					+ classLoader
					+ " (using getResourceAsStream() returned null)");
		}
		DataInputStream dstream = new DataInputStream(stream);

		try {
			try {
				return new ClassFile(dstream);
			} finally {
				dstream.close();
				stream.close();
			}
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

}
